package org.jflame.commons.util;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

import org.apache.commons.lang3.ArrayUtils;
import org.jflame.commons.valid.Validators;

/**
 * 集合工具
 *
 * @author charles.zhang
 * @see java.util.Collections
 */
public abstract class CollectionHelper {

    /**
     * 判断集合是否为null或无元素
     *
     * @param collection 集合
     * @param <E> 元素泛型
     * @return
     */
    public static <E> boolean isEmpty(Collection<E> collection) {
        return collection == null || collection.isEmpty();
    }

    /**
     * 判断集合不为null且至少有一个元素
     *
     * @param collection 集合
     * @param <E> 元素泛型
     * @return
     */
    public static <E> boolean isNotEmpty(Collection<E> collection) {
        return collection != null && !collection.isEmpty();
    }

    /**
     * 从集合中多个元素
     *
     * @param collection 集合
     * @param removeElements 要删除的元素
     * @return
     */
    public static <E> boolean removeAll(Collection<E> collection, E[] removeElements) {
        Validators.requireNonNull(collection, "collection must not be null");
        if (collection.isEmpty() || removeElements == null) {
            return false;
        }
        return collection.removeIf(p -> ArrayUtils.contains(removeElements, p));
    }

    /**
     * 判断一个对象是否在一个集合迭代内
     *
     * @param iterator 迭代器
     * @param element 要查找的对象
     * @return true存在
     */
    public static boolean contains(Iterator<?> iterator, Object element) {
        if (iterator != null) {
            while (iterator.hasNext()) {
                Object candidate = iterator.next();
                if (Objects.equals(candidate, element)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 判断一个对象是否在一个集合内
     *
     * @param collection 集合
     * @param element 要查找的对象
     * @return true存在
     */
    public static <E> boolean contains(Collection<E> collection, E element) {
        for (E object : collection) {
            if (Objects.equals(object, element)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 返回集合的最后一个元素
     *
     * @param list Collection
     * @return 集合的最后一个元素
     */
    public static <T> T getLast(List<T> list) {
        if (isEmpty(list)) {
            return null;
        }
        return list.get(list.size() - 1);
    }

    /**
     * 返回第一个非null的元素
     *
     * @param <T>
     * @param data
     * @return
     */
    public static <T> Optional<T> firstNonNull(Iterable<T> data) {
        Iterator<T> it = data.iterator();
        while (it.hasNext()) {
            T t = it.next();
            if (t != null) {
                return Optional.of(t);
            }
        }
        return Optional.empty();
    }

    /**
     * 按条件过滤集合,返回符合条件的子集List
     *
     * @param collection 集合
     * @param predicate 条件
     * @return
     */
    public static <T> List<T> subList(Collection<T> collection, Predicate<T> predicate) {
        if (isEmpty(collection)) {
            return new ArrayList<>(0);
        }
        return collection.stream()
                .filter(predicate)
                .collect(Collectors.toList());
    }

    /**
     * 按条件过滤集合,返回符合条件的子集Set
     *
     * @param collection 集合
     * @param predicate 条件
     * @return
     */
    public static <T> Set<T> subSet(Collection<T> collection, Predicate<T> predicate) {
        if (isEmpty(collection)) {
            return new HashSet<>(0);
        }
        return collection.stream()
                .filter(predicate)
                .collect(Collectors.toSet());
    }

    /**
     * 随机从列表中取N个元素
     *
     * @param <T>
     * @param srcList List
     * @param count 要取的元素个数,如果集合不够则返回所有
     * @return
     */
    public static <T> List<T> randomSubList(List<T> srcList, int count) {
        if (srcList == null) {
            throw new IllegalArgumentException("参数srcList不能为null");
        }
        int size = srcList.size();
        List<T> subList;
        if (size < count) {
            subList = new ArrayList<>(srcList);
            Collections.shuffle(subList);
            return subList;
        }

        // 生成索引列表
        List<Integer> indxs = IntStream.range(0, size)
                .boxed()
                .collect(Collectors.toList());
        // 打乱索引列表
        Collections.shuffle(indxs);

        subList = new ArrayList<>(count);
        for (int i = 0; i < count; i++) {
            subList.add(srcList.get(indxs.get(i)));
        }

        return subList;
    }

    /**
     * 随机从集合中取指定个数的元素, 如果集合元素不足count个,则返回的集合包含所有元素. 该操作不影响原集合
     *
     * @param <E> 集合元素类型
     * @param <T> 集合类型
     * @param c 原集合
     * @param count 要提取的元素个数
     * @return 返回新的集合,具体类型与原集合相同
     */
    @SuppressWarnings("unchecked")
    public static <E,T extends Collection<E>> T randomSubCollection(T c, int count) {
        if (isEmpty(c)) {
            return null;
        }

        int size = c.size();
        Collection<E> subCollection;
        try {
            subCollection = c.getClass()
                    .getConstructor()
                    .newInstance();
        } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
                | NoSuchMethodException | SecurityException e) {
            throw new RuntimeException("无法实例化集合", e);
        }

        if (size <= count) {
            subCollection.addAll(c);
            return (T) subCollection;
        }
        // 构建一个boolean集合,插入count个true值,打乱顺序.(Collections.shuffle)
        List<Boolean> boolFlags = new ArrayList<>(size);
        int i = 0;
        for (i = 0; i < size; i++) {
            boolFlags.add(i, i < count);
        }

        Collections.shuffle(boolFlags);

        Iterator<E> it = c.iterator();
        i = 0;
        int j = 0;
        while (it.hasNext()) {
            if (boolFlags.get(i++)) {
                subCollection.add(it.next());
                j++;
                if (j == count) {
                    break;
                }
            }
        }
        return (T) subCollection;
    }

    public static <E> List<E> randomSubList(Collection<E> c, int count) {
        if (isEmpty(c)) {
            return null;
        }
        List<E> subList = new ArrayList<>(count);
        int size = c.size();
        if (size <= count) {
            subList.addAll(c);
            return subList;
        }

        // 构建一个boolean集合,插入count个true值,打乱顺序.
        List<Boolean> boolFlags = new ArrayList<>(size);
        int i = 0;
        for (i = 0; i < size; i++) {
            boolFlags.add(i, i < count);
        }

        Collections.shuffle(boolFlags);

        Iterator<E> it = c.iterator();
        i = 0;
        int j = 0;
        while (it.hasNext()) {
            if (boolFlags.get(i++)) {
                subList.add(it.next());
                j++;
                if (j == count) {
                    break;
                }
            }
        }
        return subList;
    }

    /**
     * 用给定元素生成一个List
     *
     * @param elements
     * @return ArrayList
     */
    @SafeVarargs
    public static <T> List<T> newList(T... elements) {
        if (elements == null) {
            return Collections.emptyList();
        }
        ArrayList<T> lst = new ArrayList<>(elements.length);
        Collections.addAll(lst, elements);
        return lst;
    }

    /**
     * 用给定元素生成一个Set
     *
     * @param elements
     * @return HashSet
     */
    @SafeVarargs
    public static <T> Set<T> newSet(T... elements) {
        if (elements == null || elements.length == 0) {
            return Collections.emptySet();
        }

        Set<T> set = new HashSet<>(elements.length);
        Collections.addAll(set, elements);
        return set;
    }

    /**
     * 用给定元素生成一个LinkedHashSet,集合元素保持与数组相同顺序
     *
     * @param <E>
     * @param elements
     * @return
     */
    @SafeVarargs
    public static <E> Set<E> newLinkedHashSet(E... elements) {
        if (elements == null || elements.length == 0) {
            return Collections.emptySet();
        }

        if (elements.length == 1) {
            return Collections.singleton(elements[0]);
        }

        LinkedHashSet<E> set = new LinkedHashSet<E>(elements.length * 4 / 3 + 1);
        Collections.addAll(set, elements);
        return set;
    }

    /**
     * 尝试克隆一个集合.通过序列化方式克隆对象
     *
     * @param src 要克隆的集合
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> Collection<T> deepClone(Collection<T> src) {
        Collection<T> dest = null;
        try {
            ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
            ObjectOutputStream out = new ObjectOutputStream(byteOut);
            out.writeObject(src);
            ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
            ObjectInputStream in = new ObjectInputStream(byteIn);
            dest = (Collection<T>) in.readObject();
        } catch (ClassNotFoundException | IOException e) {
            throw new RuntimeException(e);
        }
        return dest;
    }

    /**
     * 判断两个集合的元素个数及元素是否相等(元素顺序可以不一致)
     *
     * @param collect1 集合1
     * @param collect2 集合2
     * @return
     */
    public static <T> boolean elementEquals(Collection<T> collect1, Collection<T> collect2) {
        if (collect1 == null) {
            return collect2 == null;
        } else {
            if (collect2 == null) {
                return false;
            }
            // 元素个数不相等
            if (collect2.size() != collect1.size()) {
                return false;
            }
            return collect1.containsAll(collect2);
        }
    }

    /**
     * 从给定的集合中,根据映射操作返回一个新的集合. 如提取元素的一个属性组成新的集合.示例:<br>
     * {@code
     * List<String> nameList = CollectionHelper.map(users, u -> u.getName());
     * }
     *
     * @param collection
     * @param mapper
     * @return
     */
    public static <T,R> List<R> map(Collection<T> collection, Function<? super T,? extends R> mapper) {
        requireNonEmpty(collection, "parameter 'collection' must not be empty");
        return collection.stream()
                .map(mapper)
                .collect(Collectors.toList());
    }

    /**
     * 从给定的集合中,根据映射操作返回一个新的集合. 如提取元素的一个属性组成新的集合.示例:<br>
     * {@code
     * List<String> nameList = CollectionHelper.map(users, u -> u.getName());
     * }
     *
     * @param collection
     * @param mapper
     * @return
     */
    public static <T,R> Set<R> mapAsSet(Collection<T> collection, Function<? super T,? extends R> mapper) {
        requireNonEmpty(collection, "parameter 'collection' must not be empty");
        return collection.stream()
                .map(mapper)
                .collect(Collectors.toSet());
    }

    /**
     * 从给定的集合中提取元素的一个int属性组成新的集合.示例: <br>
     * {@code
     * List<Integer> ageList = CollectionHelper.mapToInt(users, u -> u.getAge()); }
     *
     * @param <T>
     * @param collection
     * @param mapper
     * @return
     */
    public static <T> List<Integer> mapToInt(Collection<T> collection, Function<? super T,Integer> mapper) {
        requireNonEmpty(collection, "parameter 'collection' must not be empty");
        return collection.stream()
                .map(mapper)
                .collect(Collectors.toList());
    }

    /**
     * 集合分组
     *
     * @param collection 集合
     * @param classifier 分组条件
     * @param <K> 分组key类型
     * @param <E> 集合元素类型
     * @return
     */
    public static <K,E> Map<K,List<E>> group(Collection<E> collection, Function<? super E,? extends K> classifier) {
        return group(collection, null, classifier);
    }

    /**
     * 集合分组
     *
     * @param collection 集合
     * @param filter 过滤条件,可为null
     * @param classifier 分组条件
     * @param <K> 分组key类型
     * @param <E> 集合元素类型
     * @return
     */
    public static <K,E> Map<K,List<E>> group(Collection<E> collection, Predicate<E> filter,
            Function<? super E,? extends K> classifier) {
        if (classifier == null) {
            throw new IllegalArgumentException("classifier cannot be null");
        }
        if (collection == null) {
            return Collections.emptyMap();
        }
        Stream<E> stream = collection.stream();
        if (filter != null) {
            stream = stream.filter(filter);
        }

        return stream.collect(Collectors.groupingBy(classifier));
    }

    /**
     * 集合分组并统计每组元素个数
     *
     * @param collection 集合
     * @param classifier 分组条件
     * @param <K> 分组key类型
     * @param <E> 集合元素类型
     * @return 包含每组元素个数的 Map
     */
    public static <K,E> Map<K,Long> groupAndCount(Collection<E> collection,
            Function<? super E,? extends K> classifier) {
        return groupAndCount(collection, null, classifier);
    }

    /**
     * 集合分组并统计每组元素个数
     *
     * @param collection 集合
     * @param filter 过滤条件,可为null
     * @param classifier 分组条件
     * @param <K> 分组key类型
     * @param <E> 集合元素类型
     * @return 包含每组元素个数的 Map
     */
    public static <K,E> Map<K,Long> groupAndCount(Collection<E> collection, Predicate<E> filter,
            Function<? super E,? extends K> classifier) {
        if (classifier == null) {
            throw new IllegalArgumentException("classifier cannot be null");
        }
        if (collection == null) {
            return Collections.emptyMap();
        }
        Stream<E> stream = collection.stream();
        if (filter != null) {
            stream = stream.filter(filter);
        }

        return stream.collect(Collectors.groupingBy(classifier, Collectors.counting()));
    }

    /**
     * 断言非空集合,如果参数集合为null或无元素抛出IllegalArgumentException
     *
     * @param collection 集合
     * @param message 异常信息
     */
    public static void requireNonEmpty(Collection<?> collection, String message) {
        if (collection == null || collection.isEmpty()) {
            throw new IllegalArgumentException(message);
        }
    }

    /**
     * 集合元素用','拼接成字符串
     *
     * @param collection 集合
     * @return string
     */
    public static <E> String toString(Collection<E> collection) {
        return StringHelper.joinWithComma(collection, "[", "]");
    }
}
